#include "MemoryStack.h"

#include "LogManagement.h"

namespace EngineLib {

	MemoryStack::MemoryStack(void)
	{
		assert(false);
	}

	MemoryStack::~MemoryStack(void)
	{}

	void MemoryStack::Set(unsigned int storage_size)
	{
		_size = storage_size - sizeof(MemoryStack);
		_begin = ((unsigned char*)this) + sizeof(MemoryStack);
		_end = ((unsigned char*)this) + storage_size;
		_current_left = _begin;
		_current_right = _end;
	}

	unsigned int MemoryStack::DataSize(void) const
	{
		return _size;
	}

	unsigned int MemoryStack::StorageSize(void) const
	{
		return _size + sizeof(MemoryStack);
	}
	
	unsigned char* MemoryStack::GetBeginPointer(void) const
	{
		return _begin;
	}
	unsigned char* MemoryStack::GetEndPointer(void) const
	{
		return _end;
	}
	unsigned char* MemoryStack::GetLeftPointer(void) const
	{
		return _current_left;
	}
	unsigned char* MemoryStack::GetRightPointer(void) const
	{
		return _current_right;
	}

	unsigned int MemoryStack::GetRemainderSize(void) const
	{
		assert(_current_right - _current_left >= 0);
		return (unsigned int)(_current_right - _current_left);
	}

	void* MemoryStack::AllocateLeft(unsigned int size)
	{
		if (_current_left + size > _current_right) {
			LogManagement::Error(ERROR_CODE_STACK_OVERFLOW);
			return NULL;
		}
		void *cur = _current_left;
		_current_left += size;
		return cur;
	}

	void* MemoryStack::AllocateRight(unsigned int size)
	{
		if (_current_right - size < _current_left) {
			LogManagement::Error(ERROR_CODE_STACK_OVERFLOW);
			return NULL;
		}
		_current_right -= size;
		return _current_right;
	}

	MemoryStack* MemoryStack::CreateChildLeft(unsigned int size)
	{
		MemoryStack *ms = (MemoryStack*)AllocateLeft(size + sizeof(MemoryStack));
		if (!ms)
			return NULL;
		ms->_size = size;
		ms->_begin = ((unsigned char*)ms) + sizeof(MemoryStack);
		ms->_end = ms->_begin + size;
		ms->_current_left = ms->_begin;
		ms->_current_right = ms->_end;
		return ms;
	}

	MemoryStack* MemoryStack::CreateChildRight(unsigned int size)
	{
		MemoryStack *ms = (MemoryStack*)AllocateRight(size + sizeof(MemoryStack));
		if (!ms)
			return NULL;
		ms->_size = size;
		ms->_begin = ((unsigned char*)ms) + sizeof(MemoryStack);
		ms->_end = ms->_begin + size;
		ms->_current_left = ms->_begin;
		ms->_current_right = ms->_end;
		return ms;
	}

	MemoryStack* MemoryStack::CreateChildLeftRemainder(void)
	{
		unsigned int size = GetRemainderSize();
		if (!size) {
			LogManagement::Error(ERROR_CODE_STACK_OVERFLOW);
			return NULL;
		}
		MemoryStack *ms = (MemoryStack*)AllocateLeft(size);
		if (!ms)
			return NULL;
		ms->_size = size - sizeof(MemoryStack);
		ms->_begin = ((unsigned char*)ms) + sizeof(MemoryStack);
		ms->_end = ms->_begin + ms->_size;
		ms->_current_left = ms->_begin;
		ms->_current_right = ms->_end;
		return ms;
	}

	MemoryStack* MemoryStack::CreateChildRightRemainder(void)
	{
		unsigned int size = GetRemainderSize();
		if (!size) {
			LogManagement::Error(ERROR_CODE_STACK_OVERFLOW);
			return NULL;
		}
		MemoryStack *ms = (MemoryStack*)AllocateRight(size);
		if (!ms)
			return NULL;
		ms->_size = size - sizeof(MemoryStack);
		ms->_begin = ((unsigned char*)ms) + sizeof(MemoryStack);
		ms->_end = ms->_begin + ms->_size;
		ms->_current_left = ms->_begin;
		ms->_current_right = ms->_end;
		return ms;
	}

	void MemoryStack::FreeLeft(void)
	{
		_current_left = _begin;
	}

	void MemoryStack::FreeRight(void)
	{
		_current_right = _end;
	}

	void MemoryStack::FreeLeftTo(unsigned char *pointer)
	{
		assert(!MEM_LT(pointer, _begin));
		_current_left = pointer;
	}

	void MemoryStack::FreeRightTo(unsigned char *pointer)
	{
		assert(!MEM_GT(pointer, _end));
		_current_right = pointer;
	}

	void MemoryStack::FreeRightTo(unsigned char *pointer, unsigned int size)
	{
		assert(!MEM_GT(pointer, _end));
		_current_right = pointer + size;
	}

	void MemoryStack::FreeAll(void)
	{
		_current_left = _begin;
		_current_right = _end;
	}

}